/*
   Copyright (C) 2004/2006/2007/2008 Kai Sterker <kaisterker@linuxgames.com>
   Adapted as C library 2009 Klaus Blindert <klaus.blindert@web.de>

   Originally part of the Adonthell Project http://adonthell.linuxgames.com.
   Part of the libABF project.

   libABF is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

/**
 * @file abf_flat.c
 * @author Kai Sterker <kaisterker@linuxgames.com>
 * @author Klaus Blindert <klaus.blindert@web.de>
 *
 * @brief Flattened data representation.
 */

#include "abf_endians.h"
#include "abf_flat.h"
#include "config.h"

#include <assert.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
/* for adler32 */
#include <zlib.h>

struct abf_flat_data
{
    /// Name of the data entry
    char* Name;

    /// Type ID
    abf_data_type Type;

    /// Size
    u_int32 Size;

    /// The actual content
    char* Content;

    /// Next entry in linked list
    abf_flat_data_t *Next;
};

struct abf_flat
{
    /// Buffer storing the flattened objects
    char *Buffer;

    /// Pointer to the current position in the Buffer
    char *Ptr;

    /// Bufferspace currently used
    u_int32 Size;

    /// Maximum capacity of the Buffer
    u_int32 Capacity;

    /// Pointer to unflattened data. Valid after first call to abf_flat_parse().
    abf_flat_data_t *Data;

    /// Points to record last fetched with abf_get_*() or abf_flat_next()
    abf_flat_data_t *Decoded;
};

// names for supported data types
const char* flat_typename[NBR_TYPES] =
{
        "bool", "char", "u_int8", "s_int8", "u_int16", "s_int16",
        "u_int32", "s_int32", "string", "float", "double", "blob",
        "list"
};

void abf_flat_parse(abf_flat_t* flat);
void abf_flat_grow(abf_flat_t* flat, unsigned size);
void abf_flat_clear(abf_flat_t* flat);

void abf_error_typemismatch(abf_flat_t* flat, const char* where, const char* name,
                            abf_data_type expected, abf_data_type retrieved)
{
    fprintf (stderr, "*** %s: type mismatch for entry '%s': expected %s, retrieved %s\n", where, name,
        abf_flat_gettypename (expected),
        abf_flat_gettypename (retrieved));
}

void abf_error_missing (abf_flat_t* flat, const char* where, const char* name, abf_data_type type)
{
    fprintf (stderr, "*** %s: missing entry '%s' (of type %s)\n", where, name,
        abf_flat_gettypename (type));
}

void* abf_alloc(const char* function, unsigned needed)
{
    void *memory = malloc(needed);
    if (memory==0)
    {
        fprintf (stderr, "*** %s: failed to allocate %d more bytes.\n", function, needed);
        abort();
    }
    return memory;
}

#define CHECK_FLAT(flat) assert( flat != 0 )
#define CHECK_DATA(data) assert( data != 0 )
#define CHECKED_ALLOC(size) abf_alloc(__FUNCTION__,size);

void abf_flat_init(abf_flat_t* flat, unsigned size)
{
    CHECK_FLAT(flat);
    flat->Capacity = (size != 0 ? size : 1);
    flat->Buffer = (char*) CHECKED_ALLOC(flat->Capacity);
    memset (flat->Buffer, '\0', flat->Capacity);

    // first byte in the buffer contains the byte order
    flat->Buffer[0] = DATA_BYTE_ORDER;

    // this is the current size of content in the buffer
    flat->Size = 1;

    flat->Ptr = flat->Buffer + 1;
    flat->Data = NULL;
}

abf_flat_t* abf_create()
{
    abf_flat_t* flat = (abf_flat_t*) CHECKED_ALLOC(sizeof(abf_flat_t));
    abf_flat_init(flat,32);
    return flat;
}

void abf_destroy(abf_flat_t* flat)
{
    CHECK_FLAT(flat);
    abf_flat_clear(flat);
    free (flat->Buffer);
    free (flat);
}

void abf_copy(abf_flat_t* dst, abf_flat_t* src)
{
    CHECK_FLAT(dst);
    CHECK_FLAT(src);
    char *tmp = (char*) CHECKED_ALLOC(src->Size);
    memcpy (tmp, src->Buffer, src->Size);
    abf_flat_setbuffer (dst, tmp, src->Size);
}

void abf_flat_setbuffer(abf_flat_t* flat, char *buffer, u_int32 size)
{
    CHECK_FLAT(flat);
    abf_flat_clear (flat);
    free (flat->Buffer);
    flat->Buffer = buffer;
    flat->Capacity = size;
    flat->Ptr = flat->Buffer + 1;
    flat->Size = size;
    flat->Data = NULL;
}

void abf_flat_getbuffer(abf_flat_t* flat, char **buffer, u_int32 *size)
{
    CHECK_FLAT(flat);
    if (buffer)
      *buffer = flat->Buffer;
    if (size)
      *size = flat->Size;
}

u_int32 abf_flat_checksum(abf_flat_t* flat)
{
    u_int32 a32 = adler32 (0, NULL, 0);
    CHECK_FLAT(flat);
    return adler32 (a32, (Bytef*) flat->Buffer, flat->Size);
}

void abf_flat_clear(abf_flat_t* flat)
{
    CHECK_FLAT(flat);
    abf_flat_data_t *next = flat->Data;
    while(next)
    {
        flat->Data = next->Next;
        free (next);
        next = flat->Data;
    }
    flat->Data = NULL;
    flat->Size = 1;
    flat->Ptr = flat->Buffer + 1;
}


void abf_flat_parse(abf_flat_t* flat)
{
    int swap;
    abf_flat_data_t *first, *decoded;
    u_int32 pos, tmp32;
    u_int16 tmp16;

    CHECK_FLAT(flat);

    if (flat->Size == 1 || flat->Data != NULL) return;

    // whether we need to swap byte order or not
    swap = (flat->Buffer[0] != DATA_BYTE_ORDER);
    flat->Buffer[0] = DATA_BYTE_ORDER;

    first = 0;
    decoded = 0;
    pos = 1;
    flat->Ptr = flat->Buffer + 1;

    while (pos < flat->Size)
    {
        decoded = (abf_flat_data_t*) CHECKED_ALLOC(sizeof(abf_flat_data_t));
        if (flat->Data != NULL)
            flat->Data->Next = decoded;
        else
            first = decoded;

        decoded->Name = (char *) flat->Ptr;
        flat->Ptr += (strlen ((char*) flat->Ptr) + 1);

        decoded->Type = (abf_data_type) *((u_int8*) flat->Ptr);
        flat->Ptr += 1;

        /* deprecated type */
        if (decoded->Type == ABF_T_CHAR)
            decoded->Type = ABF_T_SINT8;

        if (swap)
        {
            // get size in correct endianess
            tmp32 = Swap32 (*((u_int32*) flat->Ptr));
            decoded->Size = tmp32;

            // update buffer
            memcpy (flat->Ptr, &tmp32, 4);
            flat->Ptr += 4;

            // check whether we need to change buffer
            switch (decoded->Type)
            {
                case ABF_T_UINT16:
                case ABF_T_SINT16:
                {
                    // update buffer
                    tmp16 = Swap16 (*((u_int16*) flat->Ptr));
                    memcpy (flat->Ptr, &tmp16, 2);
                    break;
                }
                case ABF_T_UINT32:
                case ABF_T_SINT32:
                {
                    // update buffer
                    tmp32 = Swap32 (*((u_int32*) flat->Ptr));
                    memcpy (flat->Ptr, &tmp32, 4);
                    break;
                }
                default:
                {
                    break;
                }
            }
        }
        else
        {
            decoded->Size = *((u_int32*) flat->Ptr);
            flat->Ptr += 4;
        }

        decoded->Content = flat->Ptr;
        flat->Ptr = flat->Ptr + decoded->Size;

        flat->Data = decoded;
        pos += strlen (decoded->Name) + decoded->Size + 6;
    }

    decoded->Next = NULL;
    flat->Data = first;
}

void abf_flat_grow(abf_flat_t* flat, unsigned size)
{
    char *buffer;
    CHECK_FLAT(flat);

    while(flat->Capacity < size )
        flat->Capacity *= 2;

    buffer = (char*) CHECKED_ALLOC(flat->Capacity);
    memcpy (buffer, flat->Buffer, flat->Size);
    memset (buffer + flat->Size, '\0', flat->Capacity - flat->Size);
    free(flat->Buffer);
    flat->Buffer = buffer;
    flat->Ptr = flat->Buffer + flat->Size;
}

/**
  * Enter write mode.
  * This should be called before performing any operation that
  * mofifies the contents of the flat.
  */
void abf_enter_write_mode(abf_flat_t* flat)
{
    abf_flat_data_t *next = flat->Data;
    while(next)
    {
        flat->Data = next->Next;
        free (next);
        next = flat->Data;
    }
    flat->Data = NULL;
    flat->Decoded = NULL;
}

/**
  * Enter read mode.
  * This should be called before performing any operation that
  * inspects the contents of the flat.
  */
void abf_enter_read_mode(abf_flat_t* flat)
{
    if (flat->Data == NULL)
    {
        abf_flat_parse (flat);
        flat->Decoded = flat->Data;
    }
}


void abf_flat_put (abf_flat_t* flat, const char* name, abf_data_type type, u_int32 size, const void *data)
{
    u_int8 t = type;
    u_int32 nl = strlen(name) + 1;
    u_int32 need = size + nl + 5;

    CHECK_FLAT(flat);
    abf_enter_write_mode(flat);

    if (flat->Size + need > flat->Capacity)
        abf_flat_grow (flat, flat->Size + need);

    memcpy (flat->Ptr, name, nl);
    flat->Ptr += nl;

    memcpy (flat->Ptr, &t, 1);
    memcpy (flat->Ptr + 1, &size, 4);
    memcpy (flat->Ptr + 5, data, size);

    flat->Ptr += size + 5;
    flat->Size += need;
}

abf_flat_data_t* abf_flat_get (abf_flat_t* flat, const char* name, abf_data_type type, u_int8 optional)
{
    abf_flat_data_t *result;

    CHECK_FLAT(flat);
    // enter read mode happens in abf_flat_find.
    result = abf_flat_find(flat, name);

    // in case we have a result ...
    if (result != NULL) {
        // fetch next piece of data
        flat->Decoded = flat->Decoded->Next;

        // check whether types match
        if (result->Type == type) {
            return result;
        } else {
            abf_error_typemismatch(flat, "abf_flat_get", name, type, result->Type);
            return result;
        }
    }

    // still not found -> missing error
    if (!optional)
    {
        abf_error_missing(flat, "abf_flat_get", name, type);
    }
    return NULL;
}


/// Seek to named data entry, parse if necessary, wrap if necessary, untyped.
abf_flat_data_t* abf_flat_find(abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *result = 0;
    abf_flat_data_t *current = 0;

    CHECK_FLAT(flat);
    abf_enter_read_mode(flat);


    current = flat->Decoded;

    // search from current position
    for (; flat->Decoded != NULL; flat->Decoded = flat->Decoded->Next)
        if (strcmp (flat->Decoded->Name, name) == 0) {
            result = flat->Decoded;
            break;
        }

    // not found, so restart from beginning
    if (result == NULL)
        for (flat->Decoded = flat->Data; flat->Decoded != current; flat->Decoded = flat->Decoded->Next)
            if (strcmp (flat->Decoded->Name, name) == 0) {
                result = flat->Decoded;
                break;
            }
    return result;
}

abf_flat_data_t* abf_flat_seek(abf_flat_t* flat, u_int32 i)
{
    u_int32 here = 0;

    CHECK_FLAT(flat);
    abf_enter_read_mode(flat);
    flat->Decoded = flat->Data;

    while (i != here && flat->Decoded)
    {
        flat->Decoded = flat->Decoded->Next;
        ++here;
    }
    if (i==here)
        return flat->Decoded;
    return NULL;
}

abf_flat_data_t* abf_flat_current(abf_flat_t* flat)
{
    CHECK_FLAT(flat);
    abf_enter_read_mode(flat);
    return flat->Decoded;
}

void abf_flat_advance(abf_flat_t* flat)
{
    CHECK_FLAT(flat);
    abf_enter_read_mode(flat);
    if (flat->Decoded)
        flat->Decoded = flat->Decoded->Next;
}

const char* abf_flat_gettypename(abf_data_type datatype)
{
    return flat_typename[datatype];
}

abf_data_type abf_flat_gettypeforname(const char* name)
{
    int i=0;
    for(; i!= NBR_TYPES; ++i)
      if( strcmp(flat_typename[i],name) == 0 )
          return i;
    return 0;
}

abf_data_type abf_flat_gettype(abf_flat_data_t* data)
{
    CHECK_DATA(data);
    return data->Type;
}

void abf_flat_getcontent(abf_flat_data_t* data, char**content, u_int32* size)
{
    CHECK_DATA(data);
    if (content)
      *content = data->Content;
    if (size)
      *size = data->Size;
}

char* abf_flat_getname(abf_flat_data_t* data)
{
    CHECK_DATA( data );
    return data->Name;
}

const char* abf_flat_tostring(abf_flat_data_t* data)
{
    static char buffer[32];
    CHECK_DATA(data);

    switch (abf_flat_gettype(data))
    {
        case ABF_T_BOOL:
            snprintf(buffer,31,"%d",(int) *((u_int8*) data->Content));
            return buffer;
        case ABF_T_CHAR:
        case ABF_T_SINT8:
            snprintf(buffer,31,"%d",(int) *((s_int8*) data->Content));
            return buffer;
        case ABF_T_SINT16:
            snprintf(buffer,31,"%d",(int) *((s_int16*) data->Content));
            return buffer;
        case ABF_T_SINT32:
            snprintf(buffer,31,"%d",(int) *((s_int32*) data->Content));
            return buffer;
        case ABF_T_UINT8:
            snprintf(buffer,31,"%d",(int) *((u_int8*) data->Content));
            return buffer;
        case ABF_T_UINT16:
            snprintf(buffer,31,"%d",(int) *((u_int16*) data->Content));
            return buffer;
        case ABF_T_UINT32:
            snprintf(buffer,31,"%d",(int) *((u_int32*) data->Content));
            return buffer;
        case ABF_T_STRING:
        case ABF_T_FLOAT:
        case ABF_T_DOUBLE:
            return data->Content;
        default:
            fprintf(stderr, "*** unsupported type for abf_flat_tostring:%s\n",abf_flat_gettypename(abf_flat_gettype(data)));
            return "";
    }
    return 0;
}

abf_flat_t* abf_flat_toflat(abf_flat_data_t* data)
{
    abf_flat_t* flat;
    char* buffer;

    CHECK_DATA(data);
    if (data->Type != ABF_T_FLAT)
        return 0;

    flat = abf_create();
    buffer = (char*) CHECKED_ALLOC(data->Size);
    memcpy (buffer, data->Content, data->Size);
    abf_flat_setbuffer(flat, buffer, data->Size);
    return flat;
}

void abf_fdump_with_indent(abf_flat_t* flat, FILE* stream, int indent, int recurse)
{
    int i;
    CHECK_FLAT(flat);
    abf_enter_read_mode (flat);

    abf_flat_data_t* d;
    abf_flat_t* sub;

    abf_flat_seek(flat, 0);
    for(; (d = abf_flat_current (flat)); abf_flat_advance (flat))
    {
        for(i=0; i<indent;++i)
            fprintf (stream, "  ");
        if (abf_flat_gettype(d) == ABF_T_FLAT)
        {
            fprintf (stream, "%s:%s:\n",
                abf_flat_getname (d),
                abf_flat_gettypename (abf_flat_gettype(d)));
            sub = abf_flat_toflat (d);
            if (recurse > 0)
                abf_fdump_with_indent (sub, stream, indent+1, recurse-1);
            if (recurse == ABF_NOLIMIT)
                abf_fdump_with_indent (sub, stream, indent+1, ABF_NOLIMIT);
            abf_destroy (sub);
        } else {
            fprintf(stream, "%s:%s:%s\n",
                abf_flat_getname (d),
                abf_flat_gettypename (abf_flat_gettype(d)),
                abf_flat_tostring (d));
        }
    }
}

void abf_dump (abf_flat_t* flat, int recurse)
{
    abf_fdump_with_indent (flat, stdout, 0, recurse);
}


void abf_put_bool (abf_flat_t* flat, const char* name, int b)
{
    b = b ? 1 : 0;
    abf_flat_put (flat, name, ABF_T_BOOL, 1, (void *) &b);
}

void abf_put_char (abf_flat_t* flat, const char* name, char c)
{
    abf_flat_put (flat, name, ABF_T_CHAR, 1, (void *) &c);
}

void abf_put_uint8 (abf_flat_t* flat, const char* name, u_int8 i)
{
    abf_flat_put (flat, name, ABF_T_UINT8, 1, (void*) &i);
}

void abf_put_sint8 (abf_flat_t* flat, const char* name, s_int8 i)
{
    abf_flat_put (flat, name, ABF_T_SINT8, 1, (void*) &i);
}

void abf_put_uint16 (abf_flat_t* flat, const char* name, u_int16 i)
{
    abf_flat_put (flat, name, ABF_T_UINT16, 2, (void*) &i);
}

void abf_put_sint16 (abf_flat_t* flat, const char* name, s_int16 i)
{
    abf_flat_put (flat, name, ABF_T_SINT16, 2, (void*) &i);
}

void abf_put_uint32 (abf_flat_t* flat, const char* name, u_int32 i)
{
    abf_flat_put (flat, name, ABF_T_UINT32, 4, (void*) &i);
}

void abf_put_sint32 (abf_flat_t* flat, const char* name, s_int32 i)
{
    abf_flat_put (flat, name, ABF_T_SINT32, 4, (void*) &i);
}

void abf_put_string (abf_flat_t* flat, const char* name, const char* s)
{
    abf_flat_put (flat, name, ABF_T_STRING, strlen(s) + 1, s);
}

void abf_put_float (abf_flat_t* flat, const char* name, float f)
{
    char buffer[16];
    snprintf (buffer, 15, "%.12f", f);
    abf_flat_put (flat, name, ABF_T_FLOAT, strlen(buffer) + 1, buffer);
}

void abf_put_double (abf_flat_t* flat, const char* name, double d)
{
    char buffer[32];
    snprintf (buffer, 31, "%.24g", d);
    abf_flat_put (flat, name, ABF_T_DOUBLE, strlen(buffer) + 1, buffer);
}

void abf_put_block (abf_flat_t* flat, const char* name, void *b, u_int32 size)
{
    abf_flat_put (flat, name, ABF_T_BLOB, size, b);
}

void abf_put_flat (abf_flat_t* flat, const char* name, abf_flat_t* sub)
{
    abf_flat_put (flat, name, ABF_T_FLAT, sub->Size, sub->Buffer);
}

int abf_get_bool (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_BOOL, 0);
    if (d)
      return (*((u_int8*) d->Content)) ? 1 : 0;
    return 0;
}

int abf_getopt_bool (abf_flat_t* flat, const char* name, int optional)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_BOOL, 1);
    if (d)
      return (*((u_int8*) d->Content)) ? 1 : 0;
    return optional;
}

u_int8 abf_get_uint8 (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_UINT8, 0);
    if (d)
      return *((u_int8*) d->Content);
    return 0;
}

u_int8 abf_getopt_uint8 (abf_flat_t* flat, const char* name, u_int8 optional)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_UINT8, 1);
    if (d)
      return *((u_int8*) d->Content);
    return optional;
}

s_int8 abf_get_sint8 (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_SINT8, 0);
    if (d)
      return *((s_int8*) d->Content);
    return 0;
}

s_int8 abf_getopt_sint8 (abf_flat_t* flat, const char* name, s_int8 optional)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_SINT8, 1);
    if (d)
      return *((s_int8*) d->Content);
    return optional;
}

u_int16 abf_get_uint16 (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_UINT16, 0);
    if (d)
      return *((u_int16*) d->Content);
    return 0;
}

u_int16 abf_getopt_uint16 (abf_flat_t* flat, const char* name, u_int16 optional)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_UINT16, 1);
    if (d)
      return *((u_int16*) d->Content);
    return optional;
}

s_int16 abf_get_sint16 (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_SINT16, 0);
    if (d)
      return *((s_int16*) d->Content);
    return 0;
}

s_int16 abf_getopt_sint16 (abf_flat_t* flat, const char* name, s_int16 optional)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_SINT16, 1);
    if (d)
      return *((s_int16*) d->Content);
    return optional;
}

u_int32 abf_get_uint32 (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_UINT32, 0);
    if (d)
      return *((u_int32*) d->Content);
    return 0;
}

u_int32 abf_getopt_uint32 (abf_flat_t* flat, const char* name, u_int32 optional)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_UINT32, 1);
    if (d)
      return *((u_int32*) d->Content);
    return optional;
}


s_int32 abf_get_sint32 (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_SINT32, 0);
    if (d)
        return *((s_int32*) d->Content);
    return 0;
}

s_int32 abf_getopt_sint32 (abf_flat_t* flat, const char* name, s_int32 optional)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_SINT32, 1);
    if (d)
        return *((s_int32*) d->Content);
    return optional;
}

char* abf_get_string (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_STRING, 0);
    if (d)
        return strdup (d->Content);
    return strdup ("");
}

char* abf_getopt_string (abf_flat_t* flat, const char* name, const char* optional)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_STRING, 1);
    if (d)
        return strdup (d->Content);
    return strdup (optional);
}

float abf_get_float (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_FLOAT, 0);
    if (d)
        return (float) strtod (d->Content, NULL);
    return 0.0;
}

float abf_getopt_float (abf_flat_t* flat, const char* name, float optional)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_FLOAT, 1);
    if (d)
        return (float) strtod (d->Content, NULL);
    return optional;
}

double abf_get_double (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_DOUBLE, 0);
    if (d)
        return (double) strtod (d->Content, NULL);
    return 0.0;
}

double abf_getopt_double (abf_flat_t* flat, const char* name, double optional)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_DOUBLE, 1);
    if (d)
        return (double) strtod (d->Content, NULL);
    return optional;
}


abf_flat_t* abf_get_flat (abf_flat_t* flat, const char* name)
{
    abf_flat_data_t *d = abf_flat_get (flat, name, ABF_T_FLAT, 0);
    if (d)
        return abf_flat_toflat(d);
    return abf_create();
}

